/****************************************************************************
 * @file ringbuffer.h
 * @brief Used to ringbuffer
 * @version 0.1
 * @date 2021-08-09
 *
 * @copyright Copyright (c) tuya.inc 2021
 ****************************************************************************/

#ifndef __INCLUDE_RINGBUFFER_H__
#define __INCLUDE_RINGBUFFER_H__

/****************************************************************************
 * Included Files
 ****************************************************************************/
#include <stdint.h>

 /****************************************************************************
 说明:

 1、环形队列中存储元素需为同一类型

 2、如果为单一生产者与单一消费者模型，循环队列不需使用锁、信号量等保护数据区
    如果非单一生产者+单一消费者模型，需使用锁、临界区、信号量等措施保护数据

 3、环形队列长度必须是2的N次幂，如128， 256， 512等，队列长度在初始化接口中计算,
    a. 如果使能动态内存方式，则队列长度 = 元素个数 * 单个元素大小，并向上取2的N次幂，
        因此实际的缓冲区大小不一定为初始化接口中入参定义的值
    b. 如果为静态分配，则队列长度 = 定义的缓冲区长度 / 单个元素大小，此处需注意缓冲
        区长度与元素大小的关系，最终需为2^N

 4、虽然初始化时候有传入单个元素大小并使用其计算，但是环形队列内部实际上还是以字节序
    方式进行数据存储此处使用元素大小只是为了保证效率，至少可以存储完整的一个元素的
    数据

 ****************************************************************************/


/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

//#define __TEST_ON_LINUX__

//#define __DYNAMIC_MALLOC__

#ifndef __DYNAMIC_MALLOC__
/* 参考上面说明，此处buffer长度需结合实际存储元素大小定义 */
#define RB_BUF_LEN  128
#endif // !__DYNAMIC_MALLOC__


/****************************************************************************
 * Public Type Declarations
 ****************************************************************************/

struct ringbuffer {
    uint32_t    in;
    uint32_t    out;
    uint32_t    mask;
    uint32_t    size;
    uint32_t    esize;
#ifdef __DYNAMIC_MALLOC__
    uint8_t     *data;
#else // !__DYNAMIC_MALLOC__
    uint8_t     data[RB_BUF_LEN];
#endif // !__DYNAMIC_MALLOC__
};

/****************************************************************************
 * Public Data
 ****************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

/****************************************************************************
 * Public Function Prototypes
 ****************************************************************************/

/****************************************************************************
 * rb_init()    初始化ring buffer
 * @r:          ring buffer 数据结构，为静态变量地址或堆指针
 * @len:        使能动态内存策略时，为元素个数，
 *              否则为循环队列内部buffer长度
 * @esize:      需存储的单个元素的长度，如果存储数据为单字节数据，则为1，
 *              如果存储结构体数据，则为结构体长度
 *
 * 动态内存：   计算所需长度 =  len * esize，并向上取2的N次幂作为队列长度，
 *              例如计算得到长度为28，则malloc申请内存长度为32
 * 静态数组：   计算所需长度 = RB_BUF_LEN / esize，
 *              该长度必须为2的N次幂，否则返回失败
 *
 * 返回值：     0 初始化完成
 *              -1 初始化失败
 ****************************************************************************/
int rb_init(struct ringbuffer *r, uint32_t len, uint32_t esize);

/****************************************************************************
 * rb_deinit()  初始化ring buffer
 * @r:          ring buffer 数据结构，为静态变量地址或堆指针
 *
 * 队列内部索引清空，如使能动态内存，则释放内部buffer
 *
 * 返回值：     无
 ****************************************************************************/
void rb_deinit(struct ringbuffer *r);

/****************************************************************************
 * rb_in()      将数据存入循环队列
 * @r:          ring buffer 数据结构
 * @buf:        待存储数据
 * @len:        待存储数据长度
 *
 * 返回值：     实际存入环形队列的数据长度
 *
 * Note：       如果环形队列满或者当前剩余空间不足时，
 *              实际存储数据长度 < len
 ****************************************************************************/
uint32_t rb_in(struct ringbuffer *r, const uint8_t *buf, uint32_t len);

/****************************************************************************
 * rb_out()     从环形队列中读取数据
 * @r:          ring buffer 数据结构
 * @buf:        读取数据存储buffer
 * @len:        需要读取的数据长度 or buffer 的长度
 *
 * 返回值：     实际读取到的数据长度
 *
 * Note：       如果环形队列空或者当前数据不足时，实际读取数据长度 < len
 ****************************************************************************/
uint32_t rb_out(struct ringbuffer *r, void *buf, uint32_t len);

/****************************************************************************
 * rb_unused()  获取当前队列中剩余空间
 * @r:          ring buffer 数据结构
 *
 * 返回值：     当前队列中剩余空间
 ****************************************************************************/
uint32_t rb_unused(struct ringbuffer *r);

/****************************************************************************
 * rb_avail()   当前队列中可用数据长度
 * @r:          ring buffer 数据结构
 *
 * 返回值：     队列中可用数据长度
 ****************************************************************************/
uint32_t rb_avail(struct ringbuffer *r);

/****************************************************************************
 * rb_is_empty() 当前队列是否为空
 * @r:           ring buffer 数据结构
 *
 * 返回值：      1 队列为空
 *               0 队列不空
 ****************************************************************************/
uint32_t rb_is_empty(struct ringbuffer *r);

/****************************************************************************
 * rb_is_full() 当前队列是否满
 * @r:          ring buffer 数据结构
 *
 * 返回值：     1 队列满
 *              0 队列还有存储空间
 ****************************************************************************/
uint32_t rb_is_full(struct ringbuffer *r);

/****************************************************************************
 * rb_peek()    peek
 * @r:          ring buffer 数据结构
 * @buf:        读取数据存储buffer
 * @len:        需要读取的数据长度 or buffer 的长度
 *
 * 与rb_out类似，读取ring buffer中存储的数据，但是rb_peek接口
 * 读取数据时，队列内部in/out索引不变
 *
 * 返回值：     无
 ****************************************************************************/
void rb_peek(struct ringbuffer *r, void *buf, uint32_t len);

/****************************************************************************
 * rb_peek()    clean
 * @r:          ring buffer 数据结构
 *
 * clean data
 *
 * 返回值：     无
 ****************************************************************************/
void rb_clean(struct ringbuffer *r);

#ifdef __cplusplus
}
#endif

#endif /* __INCLUDE_RINGBUFFER_H__ */

